{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Tce3stUlHN0L"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "tuOe1ymfHZPu"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MfBg1C5NB3X0"
      },
      "source": [
        "# Обучение нескольких рабочих процессов с помощью Оценщика(Estimator)\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/distribute/multi_worker_with_estimator\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Смотрите на TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ru/tutorials/distribute/multi_worker_with_estimator.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запустите в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ru/tutorials/distribute/multi_worker_with_estimator.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучайте код на GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ru/tutorials/distribute/multi_worker_with_estimator.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Скачайте ноутбук</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d3874b352184"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xHxb-dlhMIzW"
      },
      "source": [
        "## Введение\n",
        "\n",
        "Примечание: хотя вы можете использовать оценщики с `tf.distribute`, рекомендуется использовать Keras с `tf.distribute`, см. [Обучение нескольких воркеров с Keras](multi_worker_with_keras.ipynb). Обучение оценщика с помощью `tf.distribute.Strategy` имеет ограниченную поддержку.\n",
        "\n",
        "В этом руководстве описывается, как можно использовать `tf.distribute.Strategy` для распределенного обучения нескольких воркеров с помощью `tf.estimator`. Если вы пишете код с использованием `tf.estimator` и хотите масштабироваться за пределы одной машины, это руководство для вас.\n",
        "\n",
        "Перед тем, как начать, пожалуйста, прочтите руководство [распределенное обучение](../../guide/distribution_training.ipynb). \n",
        "[Учебное пособие по обучению работе с несколькими GPU](./keras.ipynb) также актуально, поскольку в этом учебном пособии используется такая же модель.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MUXex9ctTuDB"
      },
      "source": [
        "## Установка\n",
        "\n",
        "Установите TensorFlow и требуемые импорты."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "bnYxvfLD-LW-"
      },
      "outputs": [],
      "source": [
        "import tensorflow_datasets as tfds\n",
        "import tensorflow as tf\n",
        "\n",
        "import os, json"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hPBuZUNSZmrQ"
      },
      "source": [
        "## Функция ввода данных\n",
        "\n",
        "В этом руководстве используется набор данных MNIST из [TensorFlow Datasets](https://www.tensorflow.org/datasets). Код здесь похож на [учебное пособие по работе с несколькими GPU](./keras.ipynb) с одним ключевым отличием: при использовании Оценщика для обучения нескольких воркеров необходимо разпределить датасет по количеству воркеров, чтобы гарантировать сходимость модели. Входные данные разделяются по количеству воркеров, таким образом каждый воркер обрабатывает отдельные части датасета - «1/num_workers»."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "dma_wUAxZqo2"
      },
      "outputs": [],
      "source": [
        "BUFFER_SIZE = 10000\n",
        "BATCH_SIZE = 64\n",
        "\n",
        "def input_fn(mode, input_context=None):\n",
        "  datasets, info = tfds.load(name='mnist',\n",
        "                                with_info=True,\n",
        "                                as_supervised=True)\n",
        "  mnist_dataset = (datasets['train'] if mode == tf.estimator.ModeKeys.TRAIN else\n",
        "                   datasets['test'])\n",
        "\n",
        "  def scale(image, label):\n",
        "    image = tf.cast(image, tf.float32)\n",
        "    image /= 255\n",
        "    return image, label\n",
        "\n",
        "  if input_context:\n",
        "    mnist_dataset = mnist_dataset.shard(input_context.num_input_pipelines,\n",
        "                                        input_context.input_pipeline_id)\n",
        "  return mnist_dataset.map(scale).cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4BlcVXMhB59T"
      },
      "source": [
        "Еще один разумный подход к достижению сходимости - это перемешать набор данных на основании рендомного числа для каждого воркера."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8YFpxrcsZ2xG"
      },
      "source": [
        "## Конфигурация для множества рабочих процессов\n",
        "\n",
        "Одно из ключевых отличий этого руководства от [учебного пособия по работе с несколькими GPU](./keras.ipynb) - это настройка нескольких воркеров. \n",
        "Переменная среды `TF_CONFIG` - это стандартный способ указать конфигурацию кластера для каждого воркера, который является частью кластера.\n",
        "\n",
        "`TF_CONFIG` состоит из двух компонентов: `cluster` и `task`. `cluster` содержит информацию обо всем кластере, а именно о параметрах воркеров и серверов в кластере. `task` содержит информацию о текущей задаче. Первый параметр `cluster` является одним и тем же для всех рабочих процессов и серверов в кластере, а второй компонент `task` отличается для каждого рабочего сервера и определяет свой `type` и `index`. В нашем примере тип задачи - worker, а индекс задачи - 0.\n",
        "\n",
        "В целях иллюстрации в этом руководстве показано, как установить `TF_CONFIG` с двумя воркерами на localhost. На практике вы должны создать несколько воркеров на внешнем IP-адресе и порту и соответствующим образом указать это в  `TF_CONFIG` для каждого воркера, то есть изменить таск `index`.\n",
        "\n",
        "Предупреждение: *Не выполняйте этот код в Colab.* Среда выполнения TensorFlow попытается создать сервер gRPC с указанным IP-адресом и портом, что, скорее всего, завершится ошибкой.\n",
        "\n",
        "```\n",
        "os.environ['TF_CONFIG'] = json.dumps({\n",
        "    'cluster': {\n",
        "        'worker': [\"localhost:12345\", \"localhost:23456\"]\n",
        "    },\n",
        "    'task': {'type': 'worker', 'index': 0}\n",
        "})\n",
        "```\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qDreJzTffAP5"
      },
      "source": [
        "## Определение модели\n",
        "\n",
        "Напишите слои, оптимизатор и функцию потерь для обучения. Здесь мы создадим модель, также как описано в [руководстве по работе с несколькими GPU](./keras.ipynb)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WNvOn_OeiUYC"
      },
      "outputs": [],
      "source": [
        "LEARNING_RATE = 1e-4\n",
        "def model_fn(features, labels, mode):\n",
        "  model = tf.keras.Sequential([\n",
        "      tf.keras.layers.Conv2D(32, 3, activation='relu', input_shape=(28, 28, 1)),\n",
        "      tf.keras.layers.MaxPooling2D(),\n",
        "      tf.keras.layers.Flatten(),\n",
        "      tf.keras.layers.Dense(64, activation='relu'),\n",
        "      tf.keras.layers.Dense(10)\n",
        "  ])\n",
        "  logits = model(features, training=False)\n",
        "\n",
        "  if mode == tf.estimator.ModeKeys.PREDICT:\n",
        "    predictions = {'logits': logits}\n",
        "    return tf.estimator.EstimatorSpec(labels=labels, predictions=predictions)\n",
        "\n",
        "  optimizer = tf.compat.v1.train.GradientDescentOptimizer(\n",
        "      learning_rate=LEARNING_RATE)\n",
        "  loss = tf.keras.losses.SparseCategoricalCrossentropy(\n",
        "      from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(labels, logits)\n",
        "  loss = tf.reduce_sum(loss) * (1. / BATCH_SIZE)\n",
        "  if mode == tf.estimator.ModeKeys.EVAL:\n",
        "    return tf.estimator.EstimatorSpec(mode, loss=loss)\n",
        "\n",
        "  return tf.estimator.EstimatorSpec(\n",
        "      mode=mode,\n",
        "      loss=loss,\n",
        "      train_op=optimizer.minimize(\n",
        "          loss, tf.compat.v1.train.get_or_create_global_step()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "P94PrIW_kSCE"
      },
      "source": [
        "Примечание. Хотя в этом примере скорость обучения фиксирована, в целом может потребоваться регулирование скорости обучения в зависимости от глобального размера пакета"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UhNtHfuxCGVy"
      },
      "source": [
        "## MultiWorkerMirroredStrategy\n",
        "\n",
        "Для обучения модели используйте экземпляр `tf.distribute.experimental.MultiWorkerMirroredStrategy`. `MultiWorkerMirroredStrategy` создает копии всех переменных в слоях модели на каждом устройстве для всех рабочих процессов. Эта стратегия использует `CollectiveOps`, операцию TensorFlow для коллективного взаимодействия, чтобы агрегировать градиенты и синхронизировать переменные. В [руководстве по `tf.distribute.Strategy`](../../guide/distributed_training.ipynb) содержится более подробная информация об этой стратегии."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1uFSHCJXMrQ-"
      },
      "outputs": [],
      "source": [
        "strategy = tf.distribute.experimental.MultiWorkerMirroredStrategy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "H47DDcOgfzm7"
      },
      "source": [
        "## Обучение и оценка модели\n",
        "\n",
        "Укажите распределенную стратегию в `RunConfig` для оценщика, а затем обучите и оцените модель, вызвав `tf.estimator.train_and_evaluate`. В этом руководстве обучение распределяется через указание стратегии в `train_distribute`. Также возможна распределенная оценка через указание `eval_distribute`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BcsuBYrpgnlS"
      },
      "outputs": [],
      "source": [
        "config = tf.estimator.RunConfig(train_distribute=strategy)\n",
        "\n",
        "classifier = tf.estimator.Estimator(\n",
        "    model_fn=model_fn, model_dir='/tmp/multiworker', config=config)\n",
        "tf.estimator.train_and_evaluate(\n",
        "    classifier,\n",
        "    train_spec=tf.estimator.TrainSpec(input_fn=input_fn),\n",
        "    eval_spec=tf.estimator.EvalSpec(input_fn=input_fn)\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XVk4ftYx6JAO"
      },
      "source": [
        "## Оптимизация обучения\n",
        "\n",
        "Теперь у вас есть модель и оценщик, который может работать с несколькими воркерами, на основе `tf.distribute.Strategy`. Вы можете попробовать следующие методы, чтобы оптимизировать производительность обучения нескольких воркеров:\n",
        "\n",
        "* *Увеличьте размер пакета:* Размер пакета, указанный здесь, рассчитан на GPU. Как правило, рекомендуется использовать самый большой размер пакета, который умещается в памяти графического процессора.\n",
        "* *Преобразование переменных:* Преобразуйте переменные в `tf.float`, если это возможно. Официальная модель ResNet включает [пример](https://github.com/tensorflow/models/blob/8367cf6dabe11adf7628541706b660821f397dce/official/resnet/resnet_model.py#L466) того, как это можно сделать.\n",
        "* *Используйте коллективное взаимодействие:* `MultiWorkerMirroredStrategy` предоставляет несколько [реализаций коллективного взаимодействия](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/distribute/cross_device_ops.py).\n",
        "    * `RING` реализует группы на основе кольца с использованием gRPC в качестве уровня связи между хостами.\n",
        "    * `NCCL` использует [NCCL от Nvidia](https://developer.nvidia.com/nccl) для реализации групп.\n",
        "    * `AUTO` переносит выбор в процесс выполнения.\n",
        "    \n",
        "Наилучший выбор коллективной реализации зависит от количества и типа графических процессоров, а также от сетевого соединения в кластере. Чтобы отменить автоматический выбор, укажите другое допустимое значение для параметра коммуникации конструктора `MultiWorkerMirroredStrategy`, например `communication = tf.distribute.experimental.CollectiveCommunication.NCCL`.\n",
        "\n",
        "Посетите [Раздел производительности](../../guide/function.ipynb), чтобы узнать о других стратегиях и [инструментах](../../guide/profiler.md), которые можно использовать для оптимизации ваших моделей TensorFlow.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AW0Hb2xM6EGX"
      },
      "source": [
        "## Другие примеры кода\n",
        "\n",
        "1. [Полный пример](https://github.com/tensorflow/ecosystem/tree/master/distribution_strategy) для обучения нескольких воркеров в тензорфлоу экосистеме с использованием шаблонов Kubernetes. Этот пример начинается с создания модели Keras и преобразует ее в оценщик с помощью API `tf.keras.estimator.model_to_estimator`.\n",
        "2. [Официальные модели](https://github.com/tensorflow/models/tree/master/official), многие из которых можно настроить для работы с несколькими распределенными стратегиями.\n"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [
        "Tce3stUlHN0L"
      ],
      "name": "multi_worker_with_estimator.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
